/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package simhya.model.store.faststore;
import java.util.ArrayList;
import simhya.model.store.*;
import java.util.Locale;
import simhya.matheval.Evaluator;
import simhya.matheval.Expression;
import simhya.matheval.SymbolArray;


/**
 *
 * @author Luca
 */
public class FastStore implements Store {
    private Evaluator eval;
    private ArrayList<Predicate> externalPredicates;
    private ArrayList<Function> externalFunctions;
    private SymbolArray variables;
    private SymbolArray parameters;
    private boolean initialized;
    private boolean variablesInitialized;

    public FastStore() {
        this.eval = new Evaluator();
        this.externalFunctions = new ArrayList<Function>();
        this.externalPredicates = new ArrayList<Predicate>();
        this.variables = eval.getVariableReference();
        this.parameters = eval.getConstantReference();
        this.initialized = false;
        this.variablesInitialized = false;
    }

    public void addExternalFunction(Function f) {
        this.externalFunctions.add(f);
    }
    public void addExternalPredicate(Predicate p) {
        this.externalPredicates.add(p);
    }

    public int addParameter(String name, double value) {
        if (initialized)
            throw new StoreException("Store already initialized");
        if (variablesInitialized)
            throw new StoreException("Variables already initialized");
        int id = eval.addConstant(name, value);
        return id;
    }
    public int addVariable(String name, double value) {
        if (initialized)
            throw new StoreException("Store already initialized");
        if (variablesInitialized)
            throw new StoreException("Variables already initialized");
        int id = eval.addVariable(name, value);
        return id;
    }
    public void finalizeVariableInitialization() {
        variables.fixValueArray();
        parameters.fixValueArray();
        this.variablesInitialized = true;
    }
    public void finalizeInitialization() {
        for (Function f : this.externalFunctions)
            f.initialize();
        for (Predicate p : this.externalPredicates)
            p.initialize();
        this.initialized = true;
    }
    
    public ArrayList<String> getNameOfAllParameters() {
        return parameters.getNameOfAllSymbols();
    }
    public int getNumberOfParameters() {
        return parameters.getNumberOfSymbols();
    }
    public int getParameterID(String name) {
        return parameters.getSymbolId(name);
    }
    public double getParameterValue(int parameterID) {
        return parameters.getValue(parameterID);
    }
    
    public double[] getParametersValues() {
        return parameters.getReferenceToValuesArray();
    }
    public double[] getCopyOfParametersValues() {
        return parameters.getCopyOfValuesArray();
    }
    public void setAllParameterValues(ArrayList<Double> values) {
        int n = parameters.getNumberOfSymbols();
        if (values.size() != n)
            throw new StoreException("Incorrect number of parameters");
        for (int i=0;i<n;i++)
            parameters.setValue(i, values.get(i));
    }
    public void setAllParameterValues(double[] values) {
        int n = parameters.getNumberOfSymbols();
        if (values.length != n)
             throw new StoreException("Incorrect number of parameters");
        for (int i=0;i<n;i++)
            parameters.setValue(i, values[i]);
    }

    public void setAllParameterValues(Object values) {
        this.parameters.copyValues((SymbolArray)values);
    }

     public void setAllParameterValues(SymbolArray values) {
        this.parameters.copyValues(values);
    }

    public void setParameterValuesReference(Object values) {
        this.parameters.setValuesReference((SymbolArray)values);
    }

    public void setParameterValuesReference(SymbolArray values) {
        this.parameters.setValuesReference(values);
    }
    
    public void setParameterValue(int id, double value) {
        parameters.setValue(id, value);
    }

    public Object getParametersReference() {
        return this.parameters;
    }



    public ArrayList<String> getNameOfAllVariables() {
        return variables.getNameOfAllSymbols();
    }
    public int getNumberOfVariables() {
        return variables.getNumberOfSymbols();
    }
    public int getVariableID(String name) {
        return variables.getSymbolId(name);
    }
    public double getVariableValue(int id) {
        return variables.getValue(id);
    }
    
    public double[] getVariablesValues() {
        return variables.getReferenceToValuesArray();
    }
    public double[] getCopyOfVariablesValues() {
        return variables.getCopyOfValuesArray();
    }
    public void setAllVariableValues(ArrayList<Double> values) {
        int n =  variables.getNumberOfSymbols();
        if (values.size() != n)
            throw new StoreException("Incorrect number of variables");
        for (int i=0;i<n;i++)
            variables.setValue(i, values.get(i));
    }
    public void setAllVariableValues(double[] values) {
        int n =  variables.getNumberOfSymbols();
        if (values.length != n)
            throw new StoreException("Incorrect number of variables");
        for (int i=0;i<n;i++)
            variables.setValue(i, values[i]);
    }
    public void setVariableValue(int id, double value) {
        variables.setValue(id, value);
    }

    public String getVariablesRepresentation() {
        String s = "";
        ArrayList<String> names = variables.getNameOfAllSymbols();
        for (int i=0;i<this.variables.getNumberOfSymbols();i++)
            s += (i > 0 ? "; " : "") + String.format(Locale.US, "%s=%.5f", names.get(i), variables.getValue(i));
        return s;
    }

    public Object getVariablesReference() {
        return this.variables;
    }

    public void setAllVariableValues(Object values) {
        variables.copyValues((SymbolArray)values);
    }

    public void setAllVariableValues(SymbolArray values) {
        variables.copyValues(values);
    }

    public void setVariableValuesReference(Object values) {
        variables.setValuesReference((SymbolArray)values);
    }

    public void setVariableValuesReference(SymbolArray values) {
        variables.setValuesReference(values);
    }
    
    
    public Function addFunction(String function) {
        int id = -1;
        try { id = eval.parse(function); }
        catch(Exception e) { e.printStackTrace(); throw new StoreException(e.getMessage()); }
        if (id == -1)
            throw new StoreException("Failed to parse string " + function);
        Expression exp = eval.getExpression(id);
        GenericFunction f = new GenericFunction(exp);
        return f;
    }
    public void addNewFunctionDefinition(String functionDefinition) {
        try { eval.parse(functionDefinition); }
        catch(Exception e) { throw new StoreException(e.getMessage());}
    }
    public void addNewFunctionDefinition(String funcName, ArrayList<String> funcParams, String funcDef) {
        String f = "";
        f += funcName + "(";
        for (int i=0;i<funcParams.size();i++)
            f += (i>0?",":"") + funcParams.get(i);
        f+=") := " + funcDef;
        this.addNewFunctionDefinition(f);
    }
    public Predicate addPredicate(String predicate) {
        int id = -1;
        try { id = eval.parse(predicate); }
        catch(Exception e) { throw new StoreException(e.getMessage());}
        if (id == -1)
            throw new StoreException("Failed to parse string " + predicate);
        Expression exp = eval.getExpression(id);
        if (!exp.isLogicalExpression())
            throw new StoreException("Expression " + predicate + " is not a logical expression.");
        GenericPredicate p = new GenericPredicate(exp);
        return p;
    }

    
    public void newEvaluationRound() {
        this.eval.newEvaluationCode();
    }




    //unsupported

    public Object[] getVariables() {
       throw new UnsupportedOperationException("Unsupported operation. This store does not encapsulate "
               + "parameters and variables in objects, but uses a SymbolArray object");
    }
    public Object[] getParameters() {
       throw new UnsupportedOperationException("Unsupported operation. This store does not encapsulate "
               + "parameters and variables in objects, but uses a SymbolArray object");
    }
    public double computeFunction(int functionID) {
        throw new UnsupportedOperationException("Not supported by this store.");
    }
    public boolean evaluatePredicate(int predicateID) {
        throw new UnsupportedOperationException("Not supported by this store.");
    }
    public ArrayList<Integer> getVariableListOfFunction(int id) {
        throw new UnsupportedOperationException("Not supported by this store.");
    }
    public ArrayList<Integer> getVariableListOfPredicate(int id) {
        throw new UnsupportedOperationException("Not supported  by this store.");
    }
}
